package com.fuyun.rpc.client.registry;

import cn.hutool.json.JSONUtil;
import com.fuyun.rpc.client.config.RegistryConfig;
import com.fuyun.rpc.common.domain.ServiceMetaInfo;
import io.etcd.jetcd.ByteSequence;
import io.etcd.jetcd.Client;
import io.etcd.jetcd.KV;
import io.etcd.jetcd.KeyValue;
import io.etcd.jetcd.options.GetOption;
import lombok.extern.slf4j.Slf4j;

import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author: zrq1351
 * @date: 2025/2/15
 */
@Slf4j
public class EtcdRegistry implements Registry {

	/**
	 * 根节点
	 */
	private static final String ETCD_ROOT_PATH = "/fuyun-rpc/";

	private KV kvClient;

	/**
	 * 初始化与注册中心的连接 该方法使用提供的注册配置来建立与注册中心的连接，并初始化KV客户端
	 *
	 * @param registryConfig 注册中心的配置信息，包括地址、用户名、密码和超时设置
	 */
	@Override
	public void init(RegistryConfig registryConfig) {
		Client client = Client.builder().endpoints(registryConfig.getAddress())
			.user(ByteSequence.from(registryConfig.getUsername(), StandardCharsets.UTF_8))
			.password(ByteSequence.from(registryConfig.getPassword(), StandardCharsets.UTF_8))
			.connectTimeout(Duration.ofMillis(registryConfig.getTimeout())).build();
		this.kvClient = client.getKVClient();
		kvClient = client.getKVClient();
	}

	/**
	 * 根据服务键发现服务元信息列表
	 * 此方法通过在Etcd中进行前缀搜索来发现服务，前缀由ETCD_ROOT_PATH和服务键组成
	 *
	 * @param serviceKey 服务键，用于在Etcd中搜索服务信息
	 * @return 包含服务元信息的列表
	 * @throws RuntimeException 如果获取服务列表失败，抛出此异常
	 */
	@Override
	public List<ServiceMetaInfo> serviceDiscovery(String serviceKey) {
		// 前缀搜索，结尾一定要加 '/'
		String searchPrefix = ETCD_ROOT_PATH + serviceKey + "/";

		try {
			GetOption getOption = GetOption.builder().isPrefix(true).build();
			List<KeyValue> keyValues =
				kvClient.get(ByteSequence.from(searchPrefix, StandardCharsets.UTF_8), getOption).get().getKvs();
			// 解析服务信息
			return keyValues.stream().map(keyValue -> {
				String value = keyValue.getValue().toString(StandardCharsets.UTF_8);
				return JSONUtil.toBean(value, ServiceMetaInfo.class);
			}).collect(Collectors.toList());
		} catch (Exception e) {
			throw new RuntimeException("获取服务列表失败", e);
		}
	}

}